->installed_templates; } $installed_templates = array(); // get base paths $template_base_path = ( function_exists( 'WC' ) && is_callable( array( WC(), 'template_path' ) ) ) ? WC()->template_path() : apply_filters( 'woocommerce_template_path', 'woocommerce/' ); $template_base_path = untrailingslashit( $template_base_path ); $template_paths = array ( // note the order: theme before child-theme, so that child theme is always preferred (overwritten) 'default' => WPO_WCPDF()->plugin_path() . '/templates/', 'theme' => get_template_directory() . "/{$template_base_path}/pdf/", 'child-theme' => get_stylesheet_directory() . "/{$template_base_path}/pdf/", ); $template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths ); foreach ( $template_paths as $template_source => $template_path ) { $dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR ); foreach ( $dirs as $dir ) { $clean_dir = wp_normalize_path( $dir ); $template_name = basename( $clean_dir ); // let child theme override parent theme $group = ( $template_source == 'child-theme' ) ? 'theme' : $template_source; $installed_templates[ $clean_dir ] = "{$group}/{$template_name}" ; } } if ( empty( $installed_templates ) ) { // fallback to Simple template for servers with glob() disabled $simple_template_path = wp_normalize_path( $template_paths['default'] . 'Simple' ); $installed_templates[$simple_template_path] = 'default/Simple'; } $installed_templates = apply_filters( 'wpo_wcpdf_installed_templates', $installed_templates ); $this->installed_templates = $installed_templates; if ( ! empty( $this->template_list_cache ) && array_diff_assoc( $this->template_list_cache, $this->installed_templates ) ) { $this->set_template_list_cache( $this->installed_templates ); } return $installed_templates; } public function get_template_list_cache() { $template_list = get_option( 'wpo_wcpdf_installed_template_paths', array() ); if ( ! empty( $template_list ) ) { $checked_list = array(); $outdated = false; // cache could be outdated, so we check whether the folders exist foreach ( $template_list as $path => $template_id ) { if ( @is_dir( $path ) ) { $checked_list[$path] = $template_id; // folder exists continue; } $outdated = true; // folder does not exist, try replacing base if we can locate wp-content $wp_content_folder = 'wp-content'; if ( ! empty( $path ) && false !== strpos( $path, $wp_content_folder ) && defined( WP_CONTENT_DIR ) ) { // try wp-content $relative_path = substr( $path, strrpos( $path, $wp_content_folder ) + strlen( $wp_content_folder ) ); $new_path = WP_CONTENT_DIR . $relative_path; if ( @is_dir( $new_path ) ) { $checked_list[$new_path] = $template_id; } } } if ( $outdated ) { $this->set_template_list_cache( $checked_list ); } $this->installed_templates_cache = $checked_list; return $checked_list; } else { return array(); } } public function set_template_list_cache( $template_list ) { $this->template_list_cache = $template_list; update_option( 'wpo_wcpdf_installed_template_paths', $template_list ); } public function delete_template_list_cache() { delete_option( 'wpo_wcpdf_installed_template_paths' ); } public function general_settings_updated( $old_settings, $settings, $option ) { if ( is_array( $settings ) && ! empty ( $settings['template_path'] ) ) { $this->delete_template_list_cache(); $this->set_template_list_cache( $this->get_installed_templates() ); } } public function debug_settings_updated( $old_settings, $settings, $option ) { if ( is_array( $settings ) && is_array( $old_settings ) && empty( $old_settings['pretty_document_links'] ) && ! empty ( $settings['pretty_document_links'] ) ) { set_transient( 'wpo_wcpdf_flush_rewrite_rules', 'yes', HOUR_IN_SECONDS ); } } public function maybe_delete_flush_rewrite_rules_transient() { if ( get_transient( 'wpo_wcpdf_flush_rewrite_rules' ) ) { flush_rewrite_rules(); delete_transient( 'wpo_wcpdf_flush_rewrite_rules' ); } } public function get_relative_template_path( $absolute_path ) { if ( defined( 'WP_CONTENT_DIR' ) && ! empty( WP_CONTENT_DIR ) && false !== strpos( WP_CONTENT_DIR, ABSPATH ) ) { $base_path = wp_normalize_path( ABSPATH ); } else { $base_path = wp_normalize_path( WP_CONTENT_DIR ); } return str_replace( $base_path, '', wp_normalize_path( $absolute_path ) ); } public function maybe_migrate_template_paths( $settings_section = null ) { // bail if no template is selected yet (fresh install) if ( empty( $this->general_settings['template_path'] ) ) { return; } $installed_templates = $this->get_installed_templates( true ); $selected_template = wp_normalize_path( $this->general_settings['template_path'] ); $template_match = ''; if ( ! in_array( $selected_template, $installed_templates ) && substr_count( $selected_template, '/' ) > 1 ) { // search for path match foreach ( $installed_templates as $path => $template_id ) { $path = wp_normalize_path( $path ); // check if the last part of the path matches if ( substr( $path, -strlen( $selected_template ) ) === $selected_template ) { $template_match = $template_id; break; } } // fallback to template name if no path match if ( empty( $template_match ) ) { $template_ids = array_flip( array_unique( array_combine( $installed_templates, array_map( 'basename', $installed_templates ) ) ) ); $template_name = basename( $selected_template ); if ( ! empty ( $template_ids[$template_name] ) ) { $template_match = $template_ids[$template_name]; } } // migrate setting if we have a match if ( ! empty( $template_match ) ) { $this->general_settings['template_path'] = $template_match; update_option( 'wpo_wcpdf_settings_general', $this->general_settings ); /* translators: 1. path, 2. template ID */ wcpdf_log_error( sprintf( 'Template setting migrated from %1$s to %2$s', $path, $template_id ), 'info' ); } } } public function set_number_store() { check_ajax_referer( "wpo_wcpdf_next_{$_POST['store']}", 'security' ); // check permissions if ( ! $this->user_can_manage_settings() ) { die(); } $number = ! empty( $_POST['number'] ) ? (int) $_POST['number'] : 0; if ( $number > 0 ) { $number_store_method = $this->get_sequential_number_store_method(); $number_store = new Sequential_Number_Store( $_POST['store'], $number_store_method ); $number_store->set_next( $number ); echo wp_kses_post( "next number ({$_POST['store']}) set to {$number}" ); } die(); } public function get_sequential_number_store_method() { global $wpdb; $method = isset( $this->debug_settings['calculate_document_numbers'] ) ? 'calculate' : 'auto_increment'; // safety first - always use calculate when auto_increment_increment is not 1 $row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'"); if ( ! empty( $row ) && ! empty( $row->Value ) && $row->Value != 1 ) { $method = 'calculate'; } return $method; } public function schedule_yearly_reset_numbers() { if ( ! $this->maybe_schedule_yearly_reset_numbers() ) { return; } // checks AS functions existence if ( ! function_exists( 'as_schedule_single_action' ) || ! function_exists( 'as_get_scheduled_actions' ) ) { return; } $next_year = strval( intval( current_time( 'Y' ) ) + 1 ); $datetime = new \WC_DateTime( "{$next_year}-01-01 00:00:01", new \DateTimeZone( wc_timezone_string() ) ); $lock = new Semaphore( $this->lock_name, $this->lock_time, $this->lock_loggers, $this->lock_context ); $hook = 'wpo_wcpdf_schedule_yearly_reset_numbers'; // checks if there are pending actions $scheduled_actions = count( as_get_scheduled_actions( array( 'hook' => $hook, 'status' => \ActionScheduler_Store::STATUS_PENDING, ) ) ); // if no concurrent actions sets the action if ( $scheduled_actions < 1 ) { if ( $lock->lock( $this->lock_retries ) ) { $lock->log( 'Lock acquired for yearly reset numbers schedule.', 'info' ); try { $action_id = as_schedule_single_action( $datetime->getTimestamp(), $hook ); if ( ! empty( $action_id ) ) { wcpdf_log_error( "Yearly document numbers reset scheduled with the action id: {$action_id}", 'info' ); } else { wcpdf_log_error( 'The yearly document numbers reset action schedule failed!', 'critical' ); } } catch ( \Exception $e ) { $lock->log( $e, 'critical' ); } catch ( \Error $e ) { $lock->log( $e, 'critical' ); } if ( $lock->release() ) { $lock->log( 'Lock released for yearly reset numbers schedule.', 'info' ); } } else { $lock->log( 'Couldn\'t get the lock for yearly reset numbers schedule.', 'critical' ); } } else { wcpdf_log_error( "Number of concurrent yearly document numbers reset actions found: {$scheduled_actions}", 'error' ); if ( function_exists( 'as_unschedule_all_actions' ) ) { as_unschedule_all_actions( $hook ); } // reschedule $this->schedule_yearly_reset_numbers(); } } public function yearly_reset_numbers() { $lock = new Semaphore( $this->lock_name, $this->lock_time, $this->lock_loggers, $this->lock_context ); if ( $lock->lock( $this->lock_retries ) ) { $lock->log( 'Lock acquired for yearly reset numbers.', 'info' ); try { // reset numbers $documents = WPO_WCPDF()->documents->get_documents( 'all' ); $number_stores = array(); foreach ( $documents as $document ) { if ( is_callable( array( $document, 'get_sequential_number_store' ) ) ) { $number_stores[$document->get_type()] = $document->get_sequential_number_store(); } } // log reset number events if ( ! empty( $number_stores ) ) { foreach( $number_stores as $document_type => $number_store ) { if ( $number_store->get_next() === 1 ) { wcpdf_log_error( "Yearly number reset succeed for '{$document_type}' with database table name: {$number_store->table_name}", 'info' ); } else { wcpdf_log_error( "An error occurred while trying to reset yearly number for '{$document_type}' with database table name: {$number_store->table_name}", 'error' ); } } } } catch ( \Exception $e ) { $lock->log( $e, 'critical' ); } catch ( \Error $e ) { $lock->log( $e, 'critical' ); } if ( $lock->release() ) { $lock->log( 'Lock release for yearly reset numbers.', 'info' ); } } else { $lock->log( 'Couldn\'t get the lock for yearly reset numbers.', 'critical' ); } // reschedule the action for the next year $this->schedule_yearly_reset_numbers(); } public function maybe_schedule_yearly_reset_numbers() { $schedule = false; foreach ( WPO_WCPDF()->documents->get_documents( 'all' ) as $document ) { if ( isset( $document->settings['reset_number_yearly'] ) ) { $schedule = true; break; } } // unschedule existing actions if ( ! $schedule && function_exists( 'as_unschedule_all_actions' ) ) { as_unschedule_all_actions( 'wpo_wcpdf_schedule_yearly_reset_numbers' ); } return $schedule; } public function yearly_reset_action_is_scheduled() { $is_scheduled = false; $scheduled_actions = as_get_scheduled_actions( array( 'hook' => 'wpo_wcpdf_schedule_yearly_reset_numbers', 'status' => \ActionScheduler_Store::STATUS_PENDING, ) ); if ( ! empty( $scheduled_actions ) ) { $total_actions = count( $scheduled_actions ); if ( $total_actions === 1 ) { $is_scheduled = true; } else { $message = sprintf( /* translators: total scheduled actions */ __( 'Only 1 scheduled action should exist for the yearly reset of the numbering system, but %s were found', 'woocommerce-pdf-invoices-packing-slips' ), $total_actions ); wcpdf_log_error( $message ); } } return $is_scheduled; } public function get_media_upload_setting_html() { check_ajax_referer( 'wpo_wcpdf_get_media_upload_setting_html', 'security' ); // check permissions if ( ! $this->user_can_manage_settings() ) { wp_send_json_error(); } // get previous (default) args and preset current $args = $_POST['args']; $args['current'] = absint( $_POST['attachment_id'] ); if ( isset( $args['translatable'] ) ) { $args['translatable'] = wc_string_to_bool( $args['translatable'] ); } // get settings HTML ob_start(); $this->callbacks->media_upload( $args ); $html = ob_get_clean(); return wp_send_json_success( $html ); } public function move_setting_after_id( $settings, $insert_settings, $after_setting_id ) { $pos = 1; // this is already +1 to insert after the actual pos foreach ( $settings as $setting ) { if ( isset( $setting['id'] ) && $setting['id'] == $after_setting_id ) { $section = $setting['section']; break; } else { $pos++; } } // replace section if ( isset( $section ) ) { foreach ( $insert_settings as $key => $insert_setting ) { $insert_settings[$key]['section'] = $section; } } else { $empty_section = array( array( 'type' => 'section', 'id' => 'custom', 'title' => '', 'callback' => 'section', ), ); $insert_settings = array_merge( $empty_section, $insert_settings ); } // insert our api settings $new_settings = array_merge( array_slice( $settings, 0, $pos, true ), $insert_settings, array_slice( $settings, $pos, NULL, true ) ); return $new_settings; } } endif; // class_exists